package cn.wind.xboot.module.web.controller.sys;

import cn.wind.xboot.core.annotation.RestJsonController;
import cn.wind.xboot.core.constant.ApiConstant;
import cn.wind.xboot.core.json.JSON;
import cn.wind.xboot.core.res.ApiResult;
import cn.wind.xboot.core.utils.PasswordUtil;
import cn.wind.xboot.core.utils.UserUtil;
import cn.wind.xboot.logic.sys.entity.SysRole;
import cn.wind.xboot.logic.sys.entity.SysUser;
import cn.wind.xboot.logic.sys.entity.SysUserInfo;
import cn.wind.klog.annotation.Log;
import cn.wind.klog.annotation.LogAround;
import cn.wind.xboot.framework.constant.ActionConstant;
import cn.wind.xboot.framework.constant.ChannelConstant;
import cn.wind.xboot.framework.constant.ModuleConstant;
import cn.wind.xboot.framework.domain.PageVo;
import cn.wind.xboot.framework.domain.SearchVo;
import cn.wind.xboot.framework.web.controller.BaseController;
import cn.wind.xboot.module.web.handler.MyLogHandler;
import cn.wind.xboot.module.web.service.UserManage;
import cn.wind.xboot.module.web.vo.sys.SysUserVo;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.security.Principal;
import java.util.List;
import java.util.Map;

/**
 * <p>用户管理接口</p>
 * Time:10:43
 * @author xukk
 */
@Slf4j
@Api(value = "user", tags = "用户管理模块")
@RestJsonController
@Validated
@RequestMapping("user")
@CacheConfig(cacheNames = "user")
public class UserController extends BaseController<SysUser,Long> {
    @Autowired
    private ConsumerTokenServices consumerTokenServices;
    @Autowired
    private UserManage userManage;
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder){
        webDataBinder.setDisallowedFields("userInfo");
    }

    @Override
    public IService getService() {
        return sysUserService;
    }

    @PreAuthorize("hasRole('system:user:view')")
    @GetMapping("own")
    @ApiOperation(value = "获取当前登录用户接口")
    public ApiResult getOwnUserInfo(){
        SysUser sysUser= sysUserService.findUserInfoById(UserUtil.currentUserId());
        SysUserVo sysUserVo=beanMapper.map(sysUser, SysUserVo.class);
        return ApiResult.Custom().success().add(sysUserVo);
    }

    @PreAuthorize("hasRole('system:user:view')")
    @GetMapping
    @ApiOperation(value = "多条件分页获取用户列表")
    public ApiResult get(@ModelAttribute SysUserVo userVo,
                         @ModelAttribute PageVo pageVo, SearchVo searchVo){
        Map<String,Object> map= Maps.newHashMap();
        map.putAll(beanMapper.map(userVo,Map.class));
        map.putAll(beanMapper.map(searchVo,Map.class));
        IPage<SysUser> page = sysUserService.findByCondition(pageVo.init(),map);
        for(SysUser u: page.getRecords()){
            List<SysRole> list = sysRoleService.findByUserId(u.getId());
            u.setRoles(list);
            u.setPassword(null);
        }
        return ApiResult.Custom().success().add(page);
    }

    @PreAuthorize("hasRole('system:user:disable')")
    @PatchMapping("/disable/{userId}")
    @ApiOperation(value = "后台禁用用户")
    public ApiResult disable(@ApiParam("用户唯一id标识") @PathVariable Long userId){

        SysUser user= sysUserService.getById(userId);
        if(user==null){
            return ApiResult.Custom().failure("通过userId获取用户失败");
        }
        user.setEnabled(false);
        sysUserService.updateById(user);
        //手动更新缓存
        redisTemplate.delete("user::"+user.getUsername());
        return ApiResult.Custom().success();
    }

    @PreAuthorize("hasRole('system:user:view')")
    @PatchMapping( "/enable/{userId}")
    @ApiOperation(value = "后台启用用户")
    public ApiResult enable(@ApiParam("用户唯一id标识") @PathVariable Long userId){
        SysUser user= sysUserService.getById(userId);
        if(user==null){
            return ApiResult.Custom().failure("通过userId获取用户失败");
        }
        user.setEnabled(true);
        sysUserService.updateById(user);
        //手动更新缓存
        redisTemplate.delete("user::"+user.getUsername());
        return ApiResult.Custom().success();
    }

    /**
     * @param sysUserVo
     * @return
     */
    @PreAuthorize("hasRole('system:user:edit')")
    @PutMapping
    @ApiOperation(value = "修改资料",notes = "需要通过id获取原用户信息 需要username更新缓存")
    @CacheEvict(key = "#sysUserVo.username")
    public ApiResult edit(@ModelAttribute SysUserVo sysUserVo){
        return userManage.edit(sysUserVo,sysUserVo.getRoles());
    }

    @PostMapping
    @ApiOperation(value = "添加用户")
    public ApiResult save(SysUserVo sysUserVo){
        return userManage.regist(sysUserVo,sysUserVo.getRoles());
    }
    @PreAuthorize("hasRole('system:user:edit')")
    @PostMapping("/unlock")
    @ApiOperation(value = "解锁验证密码")
    public ApiResult unLock(@RequestParam String password){
        Long userId=UserUtil.currentUserId();
        SysUser u = sysUserService.getById(userId);
        if(!new BCryptPasswordEncoder().matches(password, u.getPassword())){
            return ApiResult.Custom().failure("密码不正确");
        }
        return ApiResult.Custom().success();
    }

    /**
     * 线上demo仅允许ADMIN权限改密码
     * @param id
     * @param password
     * @param newPass
     * @return
     */
    @PreAuthorize("hasRole('system:user:edit')")
    @PatchMapping("/modifyPass")
    @ApiOperation(value = "修改密码")
    public ApiResult modifyPass(@ApiParam("需用户id获取原用户数据") @RequestParam Long id,
                                @ApiParam("password") @RequestParam String password,
                                @ApiParam("新密码")  @NotBlank
                                    @Pattern(regexp = ApiConstant.MIDDLE_PASSWORD,message = "字母+数字+特殊字符(最少包含两者)，且长度要在8-16位之间")
                                    @RequestParam String newPass){
        SysUser old = sysUserService.getById(id);
        if(!PasswordUtil.matches(password,old.getPassword())){
            return ApiResult.Custom().failure("旧密码不正确");
        }
        String newEncryptPass= new BCryptPasswordEncoder().encode(newPass);
        if(!sysUserService.updatePasswordById(newEncryptPass,old.getId())){
            return ApiResult.Custom().failure("修改失败");
        }

        //手动更新缓存
        redisTemplate.delete("user::"+old.getUsername());

        return ApiResult.Custom().success();
    }

    @PreAuthorize("hasRole('system:user:view')")
    @PutMapping("own")
    @ApiOperation(value = "修改用户自己资料",notes = "用户名密码不会修改 需要通过id获取原用户信息 需要username更新缓存")
    @CacheEvict(key = "#u.username")
    public ApiResult editOwnUserInfo(@ModelAttribute SysUserVo u){
        u.init();
        SysUser sysUser=beanMapper.map(u,SysUser.class);
        if(!sysUserInfoService.update(sysUser.getUserInfo(),new UpdateWrapper<SysUserInfo>().eq("id",sysUser.getUserInfo().getId()))){
            return ApiResult.Custom().failure("修改失败");
        }
        return ApiResult.Custom().success("修改成功");
    }

    @DeleteMapping("logout")
    @JSON
    @LogAround
    @Log(module = ModuleConstant.USER,action = ActionConstant.LOGOUT,channel = ChannelConstant.BACK_WEB,value = "用户登出",fallback = MyLogHandler.class)
    public ApiResult logout(Principal principal, @RequestHeader("Authorization") String authorization) throws Exception {
        String accessToken=authorization.replace("Bearer","").trim();
        Long userId= UserUtil.currentUserId();
        redisTemplate.delete("permission::userMenuList:"+userId);
        redisTemplate.delete("userPermission::" + userId);
        consumerTokenServices.revokeToken(accessToken);
        return ApiResult.Custom().success();
    }

}
